#pragma once

#include <iostream>
#include <sys/select.h>
#include <string>
#include <functional>
#include <sys/epoll.h>
#include <unordered_map>
#include <assert.h>
#include "Epoller.hpp"
#include "err.hpp"
#include "Sock.hpp"
#include "until.hpp"

using namespace std;

namespace TCP_sv
{
    static const int defaultport = 8080;
    static const int Gnum = 64;
    class Tcpserver;
    class Connection;

    using func_t = function<void(Connection *)>;

    class Connection
    {
    public:
        Connection(int sock, Tcpserver *tcps) : _consock(sock), _tcps(tcps)
        {
        }

        ~Connection() {}

        void Register(func_t r, func_t w, func_t e) // 注册方法表，将方法传递进来
        {
            _reader = r;
            _writer = w;
            _excepter = e;
        }
        void Close()
        {
            close(_consock);
        }

    public:
        string _inbuffer;  // 输入缓冲区
        string _outbuffer; // 输出缓冲区
        func_t _reader;    // 读操作
        func_t _writer;    // 写操作
        func_t _excepter;  // 处理异常操作
        int _consock;
        Tcpserver *_tcps; // 指向Tcpserver对象的指针
    };

    class Tcpserver
    {
    private:
        void Recver(Connection *con) // 读事件
        {
            char buffer[1024];
            while (true)
            {
                ssize_t i = recv(con->_consock, buffer, sizeof(buffer) - 1, 0);
                if (i > 0)
                {
                    buffer[i] = 0;
                    con->_inbuffer += buffer;//每次读到的数据放到配套的缓冲区内
                    logMessage(DEBUG, "recv str: %s", con->_inbuffer.c_str());
                    _func(con);
                }
                else if (i == 0) // 断开连接，异常处理
                {
                    if (con->_excepter)
                    {
                        con->_excepter(con);
                        return;
                    }
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        break; // 读完了
                    }
                    else if (errno == EINTR) // 因信号中断，继续读
                    {
                        continue;
                    }
                    else
                    {
                        if (con->_excepter)
                        {
                            con->_excepter(con);
                            return;
                        }
                    }
                }
            }
        }
        void Sender(Connection *con) // 写事件
        {
            while (true)
            {
                ssize_t i = send(con->_consock, con->_outbuffer.c_str(), sizeof(con->_outbuffer), 0);
                if (i > 0)
                {
                    if (con->_outbuffer.empty()) // 内容当前send函数一次性发完了
                    {
                        logMessage(DEBUG, "sender finish");
                        con->_tcps->EnableReadWrite(con, true, false); // 发完了，把写通道关闭
                       // sleep(2);
                        break;
                    }
                    else
                    {
                        logMessage(DEBUG, "sender not finish");
                        con->_outbuffer.erase(0, i); // 如果一次性没发完，那么就将发完的部分删减掉，剩余的下次再发
                    }
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) // 上次发完了，这次再发就会err为这两个字段
                    {
                        break;
                    }
                    else if (errno == EINTR)
                    {
                        continue; // 因信号中断了，继续发送
                    }
                    else
                    {
                        logMessage(DEBUG, "excepter");
                        if (con->_excepter) // 异常了，执行异常事件
                        {
                            con->_excepter(con);
                            return;
                        }
                    }
                }
            }
        }
        void Excepter(Connection *con) // 异常事件
        {
            logMessage(DEBUG, "enter excepter");
            _epoller.Control(con->_consock, 0, EPOLL_CTL_DEL);
            con->Close();
            _Connections.erase(con->_consock);
            logMessage(DEBUG, "out excepter");
            delete con;
        }
        void Accepter(Connection *con) // 针对listenfd，将获取到的连接从底层拿到应用层
        {
            for (;;)
            {
                logMessage(DEBUG, "enter Accepter");
                string clientip;
                uint16_t clientport;
                int err = 0;
                int sock = _sock.Accpet(&clientip, &clientport, &err); // 获取成功，返回新的文件描述符用于通信，客户端的ip和port通过参数返回

                if (sock > 0)
                {
                    // 连接拿上来了，将fd添加到con对象中
                    AddConnection(sock, EPOLLIN | EPOLLET,
                                  bind(&Tcpserver::Recver, this, placeholders::_1),
                                  bind(&Tcpserver::Sender, this, placeholders::_1),
                                  bind(&Tcpserver::Excepter, this, placeholders::_1));
                    logMessage(DEBUG, "get new link,[%s:%d]", clientip.c_str(), clientport);
                }
                else
                {
                    if (err == EAGAIN || err == EWOULDBLOCK)
                        break; // 读完了
                    else if (err == EINTR)
                        continue; // 因为中断，继续读
                    else
                        break; // 错误
                }
            }
        }

        bool Isexist(int sock)
        {
            auto iter = _Connections.find(sock);
            return iter != _Connections.end(); // 判断sock是否存在connection集合中
        }

        void AddConnection(int sock, uint32_t event, func_t reader, func_t writer, func_t excepter)
        {
            if (event & EPOLLET) // 如果是ET模式，就将文件描述符设置为非阻塞
                Until::setNonBlock(sock);
            Connection *con = new Connection(sock, this);
            con->Register(reader, writer, excepter); // 把外面的函数传进去初始化内部函数

            bool n = _epoller.Add_Event(sock, event); // 告诉内核需要监管那些事件--将fd和事件添加到epoll模型中
            logMessage(DEBUG, "Add event num: %d", n);
            assert(n);
            (void)n;

            _Connections.insert(pair<int, Connection *>(sock, con)); // 将fd和con对象添加到map中进行管理
        }

        void Loop(int timeout)
        {
            // logMessage(DEBUG,"enter Loop");

            int n = _epoller.Wait(_revs, _num, timeout); // 获取已经就绪的事件
            for (int i = 0; i < n; i++)  //遍历就绪事件                // epoll_wait出错，n是-1，此时i不<n,就进不去for循环
            {                                            // 拿到就绪事件的fd和event
               // sleep(2);
               // logMessage(DEBUG, "enter Loop for");
               //提取sock和event
                int sock = _revs[i].data.fd;
                uint32_t event = _revs[i].events;
            
//处理异常事件--如果是异常事件，那么会进入读事件和写事件，但是读事件是不就绪的即读出错，就会走到处理异常的代码区。
//同样的写事件也会发送写出错，走到处理异常的代码区
                if (event & EPOLLERR)
              //      logMessage(DEBUG, "event & EPOLLERR");
                event |= (EPOLLIN | EPOLLOUT);
                if (event & EPOLLHUP)
              //      logMessage(DEBUG, "event & EPOLLHUP");

                event |= (EPOLLIN | EPOLLOUT); // 如果事件异常了，就将该事件设置为读写事件

                // listenfd事件就绪
                if ((event & EPOLLIN) && (Isexist(sock)) && (_Connections[sock]->_reader))
                {
                    logMessage(DEBUG, "con->_reader");

                    _Connections[sock]->_reader(_Connections[sock]);
                }
                if ((event & EPOLLOUT) && (Isexist(sock)) && (_Connections[sock]->_writer))
                {
                    logMessage(DEBUG, "con->_writer");

                    _Connections[sock]->_writer(_Connections[sock]);
                }
            }
         //   logMessage(DEBUG, "quit Loop");
        }

    public:
        Tcpserver(func_t fun, int port = defaultport) : _port(port), _func(fun)
        {
        }

        void inittcpserver()
        {
            logMessage(DEBUG, "enter inittcpserver\n");
            // 1.创建文件描述符
            _sock.Socket();    // 创建文件描述符--用于建立连接
            _sock.Bind(_port); // 绑定端口号和ip
            _sock.Listen();    // 将文件描述符设置为监视状态
            // 2.创建epoll模型
            _epoller.Create();
            //3.将listenfd添加到con对象，即添加到epoll中，并且注册配备的缓冲区和回调函数
            AddConnection(_sock.Fd(), EPOLLIN | EPOLLET, bind(&Tcpserver::Accepter, this, placeholders::_1), nullptr, nullptr); // 对于listenfd来说，只关心读取事件
            logMessage(DEBUG, "quit inittcpserver\n");
            _revs = new struct epoll_event[Gnum]; // 创建一个事件集合，供后续存放已经就绪的事件使用
            _num = Gnum;
        }

        void EnableReadWrite(Connection *con, bool readable, bool writable)
        {//判断
            uint32_t event = (readable ? EPOLLIN : 0) | (writable ? EPOLLOUT : 0) | EPOLLET;
            _epoller.Control(con->_consock, event, EPOLL_CTL_MOD);
        }

        void Distribute()//事件派发s
        {
            logMessage(DEBUG, "enter Distribute");
            while (true)
            {
                Loop(-1);
            }
            logMessage(DEBUG, "quit Distribute");
        }

        ~Tcpserver()
        {
            _epoller.Close();
            if (_revs != nullptr)
                delete[] _revs;
        }

    private:
        uint16_t _port;
        Sock _sock;
        Epoller _epoller;
        unordered_map<int, Connection *> _Connections; // 建立sock和connection对象的映射表
        struct epoll_event *_revs;                     // 用来存储返回的事件
        func_t _func;
        int _num; // 可监管的事件总数
    };
}